
import java.util.ArrayList;

/**
 * Almacena los distintos tipos que puedan aparecer en la gramática.
 * @author paco
 */
public class TablaTipos
{

    /**
     * Tabla de tipos
     */
    public ArrayList<Tipo> tabla;

    /**
     * Constructor de la tabla de tipos, añade los tipos básicos entero, real y booleano
     */
    public TablaTipos()
    {
        tabla = new ArrayList<Tipo>();

        Tipo t1 = new Tipo("entero", null, null, 1, null);
        Tipo t2 = new Tipo("real", null, null, 1, null);
        Tipo t3 = new Tipo("booleano", null, null, 1, null);
        Tipo t4 = new Tipo("void", null, null, 1, null);

        tabla.add(t1);
        tabla.add(t2);
        tabla.add(t3);
        tabla.add(t4);
    }

    /**
     * Busca un tipo a partir de un nombre el la tabla de tipos
     * @param nombre Nombre del tipo que se busca
     * @return Entero con la posición que ocupa el tipo en la tabla o error en caso de no existir.
     */
    public int getTipo( String nombre )
    {
        for ( int i = 0; i < tabla.size(); i++ )
        {
            if ( tabla.get(i).getNombre().equals(nombre) )
                return i;
        }
        return Tipo.ERROR;
    }

    /**
     * Desrreferencia una fila de la tabla de simbolos
     * @param tipo Fila que se quiere desrreferenciar
     * @return Tipo al que apunta la fila
     */
    public Integer desRef(int tipo)
    {
        if (tipo < 3)
        {
            return null;
        } else if (tabla.size() <= tipo)
        {
            return null;
        } else
        {
            return tabla.get(tipo).getTipoBase();
        }
    }

    /**
     * Añade un tipo metodo a la tabla de tipos
     * @param tipo Tipo que se va a añadir
     * @param base Primer parámetro que recibe o tipo de return en caso de no recibir parámetros
     * @return Posición que ocupa el tipo en la tabla
     */
    public Integer addMetodo(Integer tipo, Integer base)
    {
        Tipo t = new Tipo("metodo", tipo, null, null, base);
        tabla.add(t);

        return tabla.size();
    }

    /**
     * Añade un tipo parámetro a la tabla de tipos
     * @param tipo Tipo que se va a añadir
     * @param base Siguiente parametro de la tabla o return si es el último
     * @return Posicion que ocupa en tipo añadido en la tabla
     */
    public Integer addParametro(Integer tipo, Integer base)
    {
        Tipo t = new Tipo("parametro", tipo, null, null, base);
        tabla.add(t);

        return tabla.size();
    }

    /**
     * Añade un tipo return a la table
     * @param retorno Tipo que se va a añadir
     * @return Posicion en la tabla del tipo añadido
     */
    public Integer addRetorno(Integer retorno)
    {
        Tipo t = new Tipo("retorno", null, retorno, null, null);
        tabla.add(t);

        return tabla.size();
    }

    /**
     * Añade un nuevo tipo a la tabla de tipos
     * @param tipo Tipo base de datos que almacena el vector
     * @param aridad Dimensiones del vector
     * @return Devuelve la fila en la que se almacena la última referencia del vector
     */
    public Integer add(Integer tipo, Integer aridad)
    {
        int anterior = tipo;
        if (aridad != 0)
        {
            for (int i = 0; i < aridad; i++)
            {
                Tipo t = new Tipo("vector", null, null, 0, anterior);
                tabla.add(t);
                anterior = tabla.size() - 1;
            }
        }

        return anterior;
    }

    /**
     * Obtiene el tipo que se encuentra en una posición determinada de la tabla
     * @param indice Indice que se quiere conocer
     * @return Tipo del elemento indice
     */
    public Tipo get(int indice)
    {
        if (tabla != null && tabla.size() > indice)
        {
            return tabla.get(indice);
        } else
        {
            return null;
        }
    }

    /**
     * Comprueba si un tipo es del tipo Array
     * @param tipo Tipo que se quiere comprobar
     * @return Cierto si es un array, falso en caso contrario
     */
    public boolean isArray(int tipo)
    {
        return tabla.get(tipo).getTipo().equals("vector");
    }

    /**
     * Devuelve el tipo base al que apunta un tipo
     * @param tipo Tipo al que se hace referencia
     * @return Tipo al que apunta el tipo buscado
     */
    public int getAnterior(int tipo)
    {
        return tabla.get(tipo).getTipoBase();
    }

    /**
     * Devuelve el tamaño de un tipo
     * @param tipo Tipo del que se quiere saber el tamaño
     * @return Entero con el tamaño del tipo
     */
    public Integer getTamanyo(Integer tipo)
    {
        Tipo t = get(tipo);

        if (t.getTipoBase() == null)
        {
            return Tipo.ERROR;
        }

        int size = t.getTamanyo();
        Integer tBase = t.getTipoBase();
        t = get(tBase);
        tBase = t.getTipoBase();

        while (tBase != null)
        {
            size *= t.getTamanyo();
            tBase = t.getTipoBase();
            if (tBase != null)
            {
                t = get(tBase);
            }
        }

        return size;
    }

    /**
     * Devuelve el tipo base de un tipo
     * @param tipo Tipo del que se quiere saber su tipo base
     * @return Tipo base
     */
    public Integer getTipoBase(Integer tipo)
    {
        Tipo t = get(tipo);
        Integer tBase = t.getTipoBase();

        while (tBase != null)
        {
            t = get(tBase);
            tBase = t.getTipoBase();
        }
        return t.getTipo(t.getTipo());
    }

    /**
     * Añade una clase a la tabla de tipos
     * @param nombre Nombre de la clase
     * @return Simbolo creado con la clase
     */
    public Tipo addClase(String nombre)
    {
        Tipo t = new Tipo("clase", nombre);

        tabla.add(t);

        return t;
    }

    /**
     * Devuelve la ultima clase añadida.
     * @return Nombre de la ultima clase añadida.
     */
    public String getClaseActual()
    {
        for ( int i = tabla.size() - 1; i >= 0; i-- )
        {
            if ( tabla.get(i).isClass() )
                return tabla.get(i).getNombre();
        }
        return null;
    }
    
    /**
     * Comprueba si existe un tipo en la tabla
     * @param n Nombre del tipo a buscar
     * @return Cierto si existe, falso en caso contrario
     */
    public boolean existe ( String  n )
    {
        if ( tabla != null )
        {
            for ( Tipo t : tabla )
            {
                if ( t.getNombre().equals(n) )
                    return true;
            }
        }
        return false;
    }
    
    public String toString()
    {
        String s = "";
        if ( tabla != null )
        {
            for ( Tipo t : tabla )
            {
                s += t.toString(this);
            }
        }
        return s;
    }
}
